﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using SFML;
using SFML.Graphics;
using SFML.Window;
using Tao.OpenGl;

namespace jedlik
{
    /// <summary>
    /// Static class containing game mechanics
    /// </summary>
    public class GameMain
    {
        // I was wondering if I should put the render in the game manager. Let's not for now.
        static void Main(string[] args)
        {
            //Init debug "engine"
            Debug.Message(DebugType.Information, "Jedlik init", null, true);

            if (argparser(args))
            {
                Debug.Message(DebugType.Information, "LOL DEBUG", null);
                Debug.Logger.FinalizeLog();
                return;
            }

            Debug.Message(DebugType.Information, "Begin to set up render", null, true);
            //Init render, set it up for use, etc...
            RenderWindow Render = new RenderWindow(new VideoMode(800, 600, 32), "Jedlik teszt", Styles.Titlebar);
            Render.SetFramerateLimit(60);
            Render.PreserveOpenGLStates(true); //For now, I suck at this so...
            Render.UseVerticalSync(true); //VSync + 60 FPS limit for teh lulz
            Render.EnableKeyRepeat(false); //Different events for KeyDown and KeyUp!
            Debug.Message(DebugType.Information, "Finished setting up render", null, true);

            Debug.Message(DebugType.Information, "Beginning to bind render events", null, true);
            //Bind the events
            Render.Closed += new EventHandler(Events.RenderClosed);
            Render.Resized += new EventHandler<SizeEventArgs>(Events.RenderResized);
            Render.KeyPressed += new EventHandler<KeyEventArgs>(Events.RenderKeyDown);
            Render.KeyReleased += new EventHandler<KeyEventArgs>(Events.RenderKeyUp);
            Debug.Message(DebugType.Information, "Done binding render events", null, true);

            Debug.Message(DebugType.Information, "Setting the active window", null, true);
            //Gonna try stealing focus, bailing out if it fails
            if (!Render.SetActive(true))
	        {
                Debug.Message(DebugType.Fatal, "Couldn't gain active window status, bailing out!", null, true);
                Debug.Logger.FinalizeLog();
                return;
	        } 

            //Entering main game loop
            Debug.Message(DebugType.Information, "Entering main game loop", null, true);
            while (Render.IsOpened())
            {
                //First we dispatch the events
                Render.DispatchEvents();
                //Clear the draw buffer
                Render.Clear();
                //Although we've already called Render.Clear() I trust OpenGL more...
                Gl.glClear(Gl.GL_COLOR_BUFFER_BIT); //Also, no need to clear depth buffer, since we aren't doing 3D.


                // DO MAIN DRAWING HERE !


                //Finally, we display what we've drawn (a la flip)
                Render.Display();
            }

            Debug.Message(DebugType.Information, "Program over. Shutting down!", null);
            Debug.Logger.GetLogger().LogLine("Program shutting down.");
            Debug.Logger.FinalizeLog();
        }

        static bool argparser(string[] args)
        {
            for (int i = 0; i < args.Length; i++)
            {
                if (args[i].CompareTo("-v") == 0 || args[i].CompareTo("--version") == 0)
                {
                    return true;
                }
                if (args[i].CompareTo("-f") == 0 || args[i].CompareTo("--fullscreen") == 0)
                {
                    Debug.Message(DebugType.Information, "Window set to fullscreen by command line argument", "argparser", true);
                    GameManager.Settings.FullScreen = true;
                }
                if ((args[i].CompareTo("-w") == 0 || args[i].CompareTo("--width") == 0) && i < args.Length - 1)
                {
                    try
                    {
                        GameManager.Settings.Width = Convert.ToUInt32(args[i + 1]);
                    }
                    catch (Exception)
                    {
                        Debug.Message(DebugType.Warning, "Invalid command line setting (\'" + args[i + 1] + "\') for window width", "argparser", true);
                    }
                }
                if ((args[i].CompareTo("-h") == 0 || args[i].CompareTo("--height") == 0) && i < args.Length - 1)
                {
                    try
                    {
                        GameManager.Settings.Height = Convert.ToUInt32(args[i + 1]);
                    }
                    catch (Exception)
                    {
                        Debug.Message(DebugType.Warning, "Invalid command line setting (\'" + args[i + 1] + "\') for window height", "argparser", true);
                    }
                }
                if ((args[i].CompareTo("-b") == 0 || args[i].CompareTo("--color-depth") == 0) && i < args.Length - 1)
                {
                    try
                    {
                        GameManager.Settings.ColorDepth = Convert.ToUInt32(args[i + 1]);
                    }
                    catch (Exception)
                    {
                        Debug.Message(DebugType.Warning, "Invalid command line setting (\'" + args[i + 1] + "\') for color depth", "argparser", true);
                    }
                }
            }
            
            return false;
        }
    }

    public static class Events
    {
        /// <summary>
        /// Render Window close event handler. We ONLY explicitly handle the close itself. Shutting down other parts of the game isn't handled here.
        /// </summary>
        /// <param name="sender">Closer object. Normally the RenderWindow</param>
        /// <param name="e">Event parameters. Ignorable</param>
        public static void RenderClosed(object sender, EventArgs e)
        {
            Debug.Message(DebugType.Information, "Render closed event raised. Attemping to close render.", sender, true);
            (sender as RenderWindow).Close();
        }
        /// <summary>
        /// Render Resized event handler. Happens when the render window is resized
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event params</param>
        public static void RenderResized(object sender, SizeEventArgs e)
        {
            Debug.Message(DebugType.Information, "Render resized event raised. New size - Height: " + e.Height + " Width: " + e.Width, sender, true);
            // Not sure if this works correctly...
            Gl.glViewport(0, 0, (int)e.Width, (int)e.Height);
        }
        /// <summary>
        /// Render Window key press event handler
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event args, containing the keycode</param>
        public static void RenderKeyDown(object sender, KeyEventArgs e)
        {
            
            switch (e.Code)
            {
                case KeyCode.Escape:
                    Debug.Message(DebugType.Information, "Esc pressed, closing down render", sender, true);
                    (sender as RenderWindow).Close(); // we love c# and dynamic casting :3
                    break;
                default:
                    Debug.Message(DebugType.Information, "Key down event raised, with unmanaged key. Key code: " + e.Code.ToString() + " Modifiers: " + (e.Alt ? "Alt " : "") + (e.Control ? "Ctrl " : "") + (e.Shift ? "Shft " : ""), sender, true);
                    break;
            }
        }
        /// <summary>
        /// Render window key release event handler.
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event args containing the released key's code</param>
        public static void RenderKeyUp(object sender, KeyEventArgs e)
        {
            //TODO: implement
        }
    }

    [Serializable]
    public class JedlikException : Exception
    {
        public JedlikException() { Debug.Message(DebugType.Unknown, "An unknown exception happened" + "(unknown sender)", this); }
        public JedlikException(string message) : base(message) { Debug.Message(DebugType.Warning, message + "(unknown sender)", this); }
        public JedlikException(string message, Exception inner) : base(message, inner) { Debug.Message(DebugType.Warning, message + "(unknown sender)", this); }
        protected JedlikException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context)
            : base(info, context) { }
    }
}